package com.netflexitysolutions.amazonws.ec2.internal;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.lang.StringUtils;

import com.amazonaws.ec2.doc._2009_04_04.AvailabilityZoneItemType;
import com.amazonaws.ec2.doc._2009_04_04.AvailabilityZoneSetType;
import com.amazonaws.ec2.doc._2009_04_04.BlockDeviceMappingItemType;
import com.amazonaws.ec2.doc._2009_04_04.BlockDeviceMappingType;
import com.amazonaws.ec2.doc._2009_04_04.BundleInstanceTaskType;
import com.amazonaws.ec2.doc._2009_04_04.DescribeReservedInstancesResponseSetItemType;
import com.amazonaws.ec2.doc._2009_04_04.DescribeReservedInstancesResponseSetType;
import com.amazonaws.ec2.doc._2009_04_04.GroupItemType;
import com.amazonaws.ec2.doc._2009_04_04.GroupSetType;
import com.amazonaws.ec2.doc._2009_04_04.InstanceMonitoringStateType;
import com.amazonaws.ec2.doc._2009_04_04.InstanceStateType;
import com.amazonaws.ec2.doc._2009_04_04.IpPermissionSetType;
import com.amazonaws.ec2.doc._2009_04_04.IpPermissionType;
import com.amazonaws.ec2.doc._2009_04_04.IpRangeItemType;
import com.amazonaws.ec2.doc._2009_04_04.IpRangeSetType;
import com.amazonaws.ec2.doc._2009_04_04.LaunchPermissionItemType;
import com.amazonaws.ec2.doc._2009_04_04.LaunchPermissionListType;
import com.amazonaws.ec2.doc._2009_04_04.MonitoringInstanceType;
import com.amazonaws.ec2.doc._2009_04_04.NullableAttributeValueType;
import com.amazonaws.ec2.doc._2009_04_04.PlacementRequestType;
import com.amazonaws.ec2.doc._2009_04_04.PlacementResponseType;
import com.amazonaws.ec2.doc._2009_04_04.ProductCodeItemType;
import com.amazonaws.ec2.doc._2009_04_04.ProductCodeListType;
import com.amazonaws.ec2.doc._2009_04_04.ProductCodesSetItemType;
import com.amazonaws.ec2.doc._2009_04_04.ProductCodesSetType;
import com.amazonaws.ec2.doc._2009_04_04.RegionItemType;
import com.amazonaws.ec2.doc._2009_04_04.RegionSetType;
import com.amazonaws.ec2.doc._2009_04_04.ReservationInfoType;
import com.amazonaws.ec2.doc._2009_04_04.RunInstancesResponseType;
import com.amazonaws.ec2.doc._2009_04_04.RunningInstancesItemType;
import com.amazonaws.ec2.doc._2009_04_04.SecurityGroupItemType;
import com.amazonaws.ec2.doc._2009_04_04.SecurityGroupSetType;
import com.amazonaws.ec2.doc._2009_04_04.TerminateInstancesResponseInfoType;
import com.amazonaws.ec2.doc._2009_04_04.TerminateInstancesResponseItemType;
import com.amazonaws.ec2.doc._2009_04_04.UserDataType;
import com.amazonaws.ec2.doc._2009_04_04.UserIdGroupPairSetType;
import com.amazonaws.ec2.doc._2009_04_04.UserIdGroupPairType;
import com.netflexitysolutions.amazonws.ec2.AvailabilityZone;
import com.netflexitysolutions.amazonws.ec2.BlockDeviceMapping;
import com.netflexitysolutions.amazonws.ec2.BundleInstanceInfo;
import com.netflexitysolutions.amazonws.ec2.Error;
import com.netflexitysolutions.amazonws.ec2.IPPermission;
import com.netflexitysolutions.amazonws.ec2.IPProtocol;
import com.netflexitysolutions.amazonws.ec2.InstanceMonitoringState;
import com.netflexitysolutions.amazonws.ec2.InstanceState;
import com.netflexitysolutions.amazonws.ec2.InstanceTerminationResult;
import com.netflexitysolutions.amazonws.ec2.InstanceType;
import com.netflexitysolutions.amazonws.ec2.LaunchPermission;
import com.netflexitysolutions.amazonws.ec2.Region;
import com.netflexitysolutions.amazonws.ec2.ReservedInstances;
import com.netflexitysolutions.amazonws.ec2.RunningInstance;
import com.netflexitysolutions.amazonws.ec2.SecurityGroup;
import com.netflexitysolutions.amazonws.ec2.Storage;
import com.netflexitysolutions.amazonws.ec2.UserIdGroupPair;

public class ModelUtil {
	
	/**
	 * @param productCodes
	 * @return
	 */
	public static List<String> toDomainType(ProductCodeListType productCodes) {
		if(productCodes == null) {
			return null;
		}
		
		List<ProductCodeItemType> items = productCodes.getItem();
		List<String> result = new ArrayList<String>();
		for (ProductCodeItemType productCodeItemType : items) {
			result.add(productCodeItemType.getProductCode());
		}
		return result;
	}
	
	/**
	 * @param productCodes
	 * @return
	 */
	public static Set<String> toDomainType(ProductCodesSetType productCodes) {
		if(productCodes == null) {
			return null;
		}
		
		List<ProductCodesSetItemType> items = productCodes.getItem();
		Set<String> result = new HashSet<String>();
		for (ProductCodesSetItemType productCodeItemType : items) {
			result.add(productCodeItemType.getProductCode());
		}
		return result;
	}
	
	public static List<LaunchPermission> toDomainType(LaunchPermissionListType launchPermissions) {
		if(launchPermissions == null) {
			return null;
		}
		
		List<LaunchPermissionItemType> items = launchPermissions.getItem();
		List<LaunchPermission> result = new ArrayList<LaunchPermission>();
		for (LaunchPermissionItemType launchPermissionItemType : items) {
			result.add(toDomainType(launchPermissionItemType));
		}
		return result;
	}
	
	public static LaunchPermission toDomainType(LaunchPermissionItemType launchPermission) {
		if(launchPermission == null) {
			return null;
		}
		
		LaunchPermission result;
		if (StringUtils.isNotEmpty(launchPermission.getGroup())) {
			result = new LaunchPermission(LaunchPermission.Type.GROUP, launchPermission.getGroup());
		} else {
			result = new LaunchPermission(LaunchPermission.Type.USER, launchPermission.getUserId());
		}
		return result;
	}
	
	public static LaunchPermissionListType toXMLType(List<LaunchPermission> launchPermissions) {
		if(launchPermissions == null) {
			return null;
		}
		
		LaunchPermissionListType result = new LaunchPermissionListType();
		List<LaunchPermissionItemType> items = result.getItem();
		for (LaunchPermission permission : launchPermissions) {
			items.add(toXMLType(permission));
		}
		return result;
	}
	
	public static LaunchPermissionItemType toXMLType(LaunchPermission launchPermission) {
		LaunchPermissionItemType result = new LaunchPermissionItemType();
		switch (launchPermission.getType()) {
		case USER:
			result.setUserId(launchPermission.getValue());
			break;

		case GROUP:
			result.setGroup(launchPermission.getValue());
			break;
		}
		return result;
	}
	
	public static String toDomainType(NullableAttributeValueType value) {
		if(value == null) {
			return null;
		}
		
		return value.getValue();
	}
	
	public static List<BlockDeviceMapping> toDomainType(BlockDeviceMappingType type) {
		if(type == null) {
			return null;
		}
		
		List<BlockDeviceMapping> result = new ArrayList<BlockDeviceMapping>();
		List<BlockDeviceMappingItemType> mappings  = type.getItem();
		for (BlockDeviceMappingItemType mapping : mappings) {
			result.add(new BlockDeviceMapping(mapping.getDeviceName(), mapping.getVirtualName()));
		}
		return result;
	}
	
	public static List<RunningInstance> toDomainType(ReservationInfoType reservation) {
		if(reservation == null || reservation.getInstancesSet() == null) {
			return null;
		}
		
		List<RunningInstance> result = new ArrayList<RunningInstance>();
		
		String ownerId = reservation.getOwnerId();
		String reservationId = reservation.getReservationId();
		Set<String> groups = toDomainType(reservation.getGroupSet());
		List<RunningInstancesItemType> list = reservation.getInstancesSet().getItem();
		for (RunningInstancesItemType item : list) {
			result.add(toDomainType(item, ownerId, reservationId, groups));
		}
		return result;
	}
	
	public static List<RunningInstance> toDomainType(RunInstancesResponseType reservation) {
		if(reservation == null || reservation.getInstancesSet() == null) {
			return null;
		}
		
		List<RunningInstance> result = new ArrayList<RunningInstance>();
		
		String ownerId = reservation.getOwnerId();
		String reservationId = reservation.getReservationId();
		Set<String> groups = toDomainType(reservation.getGroupSet());
		List<RunningInstancesItemType> list = reservation.getInstancesSet().getItem();
		for (RunningInstancesItemType item : list) {
			result.add(toDomainType(item, ownerId, reservationId, groups));
		}
		return result;
	}
	
	public static Set<String> toDomainType(GroupSetType groups) {
		if(groups == null) {
			return null;
		}
		
		Set<String> result = new HashSet<String>();
		List<GroupItemType> list = groups.getItem();
		for (GroupItemType groupItemType : list) {
			result.add(groupItemType.getGroupId());
		}
		return result;
	}
	
	public static RunningInstance toDomainType(RunningInstancesItemType instance, 
										String ownerId, String reservationId, Set<String> securityGroups) {
		if(instance == null) {
			return null;
		}		
		
		RunningInstance result = new RunningInstance();
		result.setAmiLaunchIndex(instance.getAmiLaunchIndex());
		result.setDnsName(instance.getDnsName());
		result.setId(instance.getInstanceId());
		result.setImageId(instance.getImageId());
		result.setKernelId(instance.getKernelId());
		result.setKeyName(instance.getKeyName());
		result.setLaunchTime(toDomainType(instance.getLaunchTime()));
		result.setMonitoringState(toDomainType(instance.getMonitoring()));
		result.setOwnerId(ownerId);
		result.setPlacement(toDomainType(instance.getPlacement()));
		result.setPlatform(instance.getPlatform());
		result.setPrivateDnsName(instance.getPrivateDnsName());
		result.setProductCodes(toDomainType(instance.getProductCodes()));
		result.setRamdiskId(instance.getRamdiskId());
		result.setReason(instance.getReason());
		result.setReservationId(reservationId);
		result.setSecurityGroups(securityGroups);
		result.setState(toDomainType(instance.getInstanceState()));
		result.setType(InstanceType.instanceTypeForName(instance.getInstanceType()));
		return result;
	}
	
	public static Date toDomainType(XMLGregorianCalendar date) {
		if(date == null) {
			return null;
		}
		
		return date.toGregorianCalendar().getTime();
	}
	
	public static InstanceMonitoringState toDomainType(InstanceMonitoringStateType state) {
		if(state == null) {
			return null;
		}
				
		return InstanceMonitoringState.instanceMonitoringStateForName(state.getState());
	}
	
	public static InstanceState toDomainType(InstanceStateType type) {
		if(type == null) {
			return null;
		}
		
		return InstanceState.instanceStateForCode(type.getCode());
	}
	
	public static String toDomainType(PlacementResponseType placement) {
		if(placement == null) {
			return null;
		}
		
		return placement.getAvailabilityZone();
	}
	
	public static List<InstanceTerminationResult> toDomainType(TerminateInstancesResponseInfoType instancesSet) {
		if(instancesSet == null) {
			return null;
		}
		
		List<InstanceTerminationResult> result = new ArrayList<InstanceTerminationResult>();
		List<TerminateInstancesResponseItemType> list = instancesSet.getItem();
		for (TerminateInstancesResponseItemType item : list) {
			result.add(toDomainType(item));
		}
		return result;
	}
	
	public static InstanceTerminationResult toDomainType(TerminateInstancesResponseItemType instance) {
		if(instance == null) {
			return null;
		}
		
		InstanceTerminationResult result = new InstanceTerminationResult();
		result.setInstanceId(instance.getInstanceId());
		result.setShutdownState(toDomainType(instance.getShutdownState()));
		result.setPreviousState(toDomainType(instance.getPreviousState()));
		return result;
	}
	
	public static BlockDeviceMappingType toXMLType(List<BlockDeviceMapping> mappings) {
		if(mappings == null) {
			return null;
		}
		
		BlockDeviceMappingType result = new BlockDeviceMappingType();
		List<BlockDeviceMappingItemType> list = result.getItem();
		for (BlockDeviceMapping mapping : mappings) {
			BlockDeviceMappingItemType item = new BlockDeviceMappingItemType();
			item.setDeviceName(mapping.getDeviceName());
			item.setVirtualName(mapping.getVirtualName());
			list.add(item);
		}
		return result;
	}
	
	public static GroupSetType toXMLType(Set<String> securityGroups) {
		if(securityGroups == null) {
			return null;
		}
		
		GroupSetType result = new GroupSetType();
		List<GroupItemType> list = result.getItem();
		for (String group : securityGroups) {
			GroupItemType item = new GroupItemType();
			item.setGroupId(group);
			list.add(item);
		}
		return result;
	}
	
	public static MonitoringInstanceType toXMLType(Boolean state) {
		if(state == null) {
			return null;
		}
		
		MonitoringInstanceType result = new MonitoringInstanceType();
		result.setEnabled(state);
		return result;
	}
	
	public static UserDataType toXMLType(String userData) {
		if(userData == null) {
			return null;
		}
		
		UserDataType result = new UserDataType();
		result.getContent().add(userData);
		
		return result;
	}
	
	public static PlacementRequestType toXMLType(PlacementRequestType xmlType, String availabilityZone) {
		if(availabilityZone == null) {
			return null;
		}
		
		PlacementRequestType result = xmlType != null ? xmlType : new PlacementRequestType();
		result.setAvailabilityZone(availabilityZone);
		
		return result;
	}
	
	public static List<ReservedInstances> toDomainType(DescribeReservedInstancesResponseSetType instances) {
		if(instances == null) {
			return null;
		}
		
		List<ReservedInstances> result = new ArrayList<ReservedInstances>();
		List<DescribeReservedInstancesResponseSetItemType> list = instances.getItem();
		for (DescribeReservedInstancesResponseSetItemType item : list) {
			result.add(toDomainType(item));
		}
		return result;
	}
	
	public static ReservedInstances toDomainType(DescribeReservedInstancesResponseSetItemType instance) {
		if(instance == null) {
			return null;
		}
		
		ReservedInstances result = new ReservedInstances();
		result.setAvailabilityZone(instance.getAvailabilityZone());
		result.setDuration(instance.getDuration());
		result.setFixedPrice(instance.getFixedPrice());
		result.setInstanceCount(instance.getInstanceCount().intValue()); //according to xsd this element cannot be null
		result.setInstanceType(InstanceType.instanceTypeForName(instance.getInstanceType()));
		result.setProductDescription(instance.getProductDescription());
		result.setReservedInstancesId(instance.getReservedInstancesId());
		result.setStart(toDomainType(instance.getStart()));
		result.setState(instance.getState());
		result.setUsagePrice(instance.getUsagePrice());
		
		return result;
	}
	
	public static List<SecurityGroup> toDomainType(SecurityGroupSetType groups) {
		if(groups == null) {
			return null;
		}
		
		List<SecurityGroup> result = new ArrayList<SecurityGroup>();
		List<SecurityGroupItemType> list = groups.getItem();
		for (SecurityGroupItemType item : list) {
			result.add(toDomainType(item));
		}
		return result;
	}
	
	public static SecurityGroup toDomainType(SecurityGroupItemType group) {
		if(group == null) {
			return null;
		}
		
		SecurityGroup result = new SecurityGroup();
		result.setDescription(group.getGroupDescription());
		result.setIpPermisions(toDomainType(group.getIpPermissions()));
		result.setName(group.getGroupName());
		result.setOwnerId(group.getOwnerId());
		return result;
	}
	
	public static List<IPPermission> toDomainType(IpPermissionSetType permisions) {
		if(permisions == null) {
			return null;
		}
		
		List<IPPermission> result = new ArrayList<IPPermission>();
		List<IpPermissionType> list = permisions.getItem();
		for (IpPermissionType item : list) {
			result.add(toDomainType(item));
		}
		return result;
	}
	
	public static IPPermission toDomainType(IpPermissionType permision) {
		if(permision == null) {
			return null;
		}
		
		IPPermission result = new IPPermission();
		result.setFromPort(permision.getFromPort());
		result.setIpRanges(toDomainType(permision.getIpRanges()));
		result.setProtocol(IPProtocol.ipProtocolForName(permision.getIpProtocol()));
		result.setToPort(permision.getToPort());
		result.setUserIdGroupPairs(toDomainType(permision.getGroups()));
		return result;
	}
	
	public static Set<String> toDomainType(IpRangeSetType ranges) {
		if(ranges == null) {
			return null;
		}
		
		Set<String> result = new HashSet<String>();
		List<IpRangeItemType> list = ranges.getItem();
		for (IpRangeItemType item : list) {
			if (item == null) {
				continue;
			}
			result.add(item.getCidrIp());
		}
		return result;
	}
	
	public static Set<UserIdGroupPair> toDomainType(UserIdGroupPairSetType pairs) {
		if(pairs == null) {
			return null;
		}
		
		Set<UserIdGroupPair> result = new HashSet<UserIdGroupPair>();
		List<UserIdGroupPairType> list = pairs.getItem();
		for (UserIdGroupPairType item : list) {
			if (item == null) {
				continue;
			}
			result.add(new UserIdGroupPair(item.getUserId(), item.getGroupName()));
		}
		return result;
	}
	
	public static IpPermissionType toXMLType(IPPermission permission) {
		IpPermissionType result = new IpPermissionType();
		result.setFromPort(permission.getFromPort());
		result.setIpProtocol(permission.getProtocol() != null ? permission.getProtocol().getName() : "");
		result.setToPort(permission.getToPort());
		
		IpRangeSetType ipSet = new IpRangeSetType();
		result.setIpRanges(ipSet);
		if (permission.getIpRanges() != null) {
			List<IpRangeItemType> ipList = ipSet.getItem();
			for (String range : permission.getIpRanges()) {
				IpRangeItemType item = new IpRangeItemType();
				item.setCidrIp(range);
				ipList.add(item);
			}
		}
		
		UserIdGroupPairSetType pairSet = new UserIdGroupPairSetType();
		result.setGroups(pairSet);
		if (permission.getUserIdGroupPairs() != null) {
			List<UserIdGroupPairType> pairList = pairSet.getItem();
			for (UserIdGroupPair pair : permission.getUserIdGroupPairs()) {
				UserIdGroupPairType item = new UserIdGroupPairType();
				item.setGroupName(pair.getGroupName());
				item.setUserId(pair.getUserId());
				pairList.add(item);
			}
		}
		
		return result;
	}
	
	public static List<AvailabilityZone> toDomainType(AvailabilityZoneSetType zones) {
		if(zones == null) {
			return null;
		}
		
		List<AvailabilityZone> result = new ArrayList<AvailabilityZone>();
		List<AvailabilityZoneItemType> list = zones.getItem();
		for (AvailabilityZoneItemType item : list) {
			if (item == null) {
				continue;
			}
			result.add(new AvailabilityZone(item.getZoneName(), item.getZoneState(), item.getRegionName()));
		}
		return result;
	}
	
	public static List<Region> toDomainType(RegionSetType regions) {
		if(regions == null) {
			return null;
		}
		
		List<Region> result = new ArrayList<Region>();
		List<RegionItemType> list = regions.getItem();
		for (RegionItemType item : list) {
			if (item == null) {
				continue;
			}
			result.add(new Region(item.getRegionName(), item.getRegionEndpoint()));
		}
		return result;
	}
	
	public static BundleInstanceInfo toDomainType(BundleInstanceTaskType type) {
		if(type == null) {
			return null;
		}
		
		Error error = null;
		if (type.getError() != null) {
			error = new Error(type.getError().getCode(), type.getError().getMessage());
		}
		Storage storage = null;
		if (type.getStorage() != null && type.getStorage().getS3() != null) {
			storage = new Storage(type.getStorage().getS3().getBucket(), type.getStorage().getS3().getPrefix());
		}
		return new BundleInstanceInfo(type.getInstanceId(), type.getBundleId(), type.getState(), 
				ModelUtil.toDomainType(type.getStartTime()),
				ModelUtil.toDomainType(type.getUpdateTime()),
				type.getProgress(), storage, error);
	}
}
